<h3>Chapter 2: The LPMud Driver</h3>
<p><strong>2.1 Review of Basic Driver/Mudlib Interaction</strong></p>
<p>
In the LPC Basics textbook, you learned a lot about the way the mudlib works,
specifically in relation to objects you code in order to build your realm.
Not much was discussed about the interaction between the mudlib and the
driver. You should know, however, that the driver does the following:
</p>
<ul>
  <li>1) When an object is first loaded into memory, the driver will call create()
      in native muds and reset() in compat muds. A creator uses create() or reset()
      to give initial values to the object.
  <li>2) At an interval setup by the game administrator, the driver
      calls the function reset(). This allows the object to regenerate monsters
      and such. Notice that in a compat mud, the same function is used to set
      up initial values as is used to reset the room.
  <li>3) Any time a living object comes in contact with an object of any sort,
      the driver calls init() in the newly encountered object. This allows newly
      encountered objects to give living objects commands to execute through the
      add_action() efun, as well as perform other actions which should happen
      whenever a living thing encounters a given object.
  <li>4) The driver defines a set of functions known as efuns which
      are available to all objects in the game. Examples of commonly used efuns
      are: this_player(), this_object(), write(), say(), etc.
</ul>
<hr size="1">
<p><strong>2.2 The Driver Cycle</strong></p>
<p>
The driver is a C program which runs the game. Its basic functions are to
accept connections from the outside world so people can login, interpret the
LPC code which defines LPC objects and how they function in the game, and
accept user input and call the appropriate LPC functions which match the
event. In its most simplest essence, it is an unending loop.
</p>
<p>
Once the game has booted up and is properly functioning (the boot up process
will be discussed in a future, advanced LPC textbook), the driver enters
a loop which does not terminate until the shutdown() efun is legally called
or a bug causes the driver program to crash. First off, the driver handles
any new incoming connections and passes control of the connection to a login
object. After that, the driver puts together a table of commands which have
been entered by users since the last cycle of the driver. After the command
table is assembled, all messages scheduled to be sent to the connection
from the last driver cycle are sent out to the user. At this point, the
driver goes through the table of commands to be executed and executes each
set of commands each object has stored there. The driver ends its cycle
by calling the function heart_beat() in every object with a heart_beat()
set and finally performing all pending call outs. This chapter will not
deal with the handling of connections, but instead will focus on how the
driver handles user commands and heartbeats and call outs.
</p>
<hr size="1">
<p><strong>2.3 User Commands</strong></p>
<p>
As noted in section 1.2, the driver stores a list of
commands for each user to be executed each cycle. The commands list has
the name of the living object performing the command, the object which gave
the living object that command, and the function which is to be executed
in order to perform the command. The driver refers to the object which typed
in the command as the command giver. It is the command giver which gets
returned as this_player() in most cases.
</p>
<p>
The driver starts at the top of the list of living objects with pending
commands, and successively performs each command it typed by calling the
function associated with the command and passing any arguments the command
giver gave as arguments to the function. As the driver starts with the commands
issued by a new living object, the command giver variable is changed to
be equal to the new living object, so that during the sequence of functions
initiated by that command, the efun this_player() returns the object which
issued the command.
</p>
<p>
Let's look at the command buffer for an example player. Since the execution
of his last command, Bozo has typed "north" and "tell descartes when is
the next reboot". The command "north" is associated with the function "Do_Move()"
in the room Bozo is in (the command "north" is automatically setup by the
set_exits() efun in that room). The command "tell" is not specifically listed
as a command for the player, however, in the player object there is a function
called "cmd_hook()" which is associated with the command "", which matches
any possible user input.
</p>
<p>
Once the driver gets down to Bozo, the command giver variable is set to
the object which is Bozo. Then, seeing Bozo typed "north" and the function
"north" is associated with, the driver calls Bozo's_Room- >Do_Move(0). An
argument of 0 is passed to the function since Bozo only typed the command
"north" with no arguments. The room naturally calls some functions it needs,
all the while such that the efun this_player() returns the object which
is Bozo. Eventually, the room object will call move_player() in Bozo, which
in turn calls the move_object() efun. This efun is responsible for changing
an object's environment.
</p>
<p>
When the environment of an object changes, the commands available to it
from objects in its previous environment as well as from its previous environment
are removed from the object. Once that is done, the driver calls the efun
init() in the new environment as well as in each object in the new environment.
During each of these calls to init(), the object Bozo is still the command
giver. Thus all add_action() efuns from this move will apply to Bozo. Once
all those calls are done, control passes back from the move_object() efun
to the move_player() lfun in Bozo. move_player() returns control back to
Do_Move() in the old room, which returns 1 to signify to the driver that
the command action was successful. If the Do_Move() function had returned
0 for some reason, the driver would have written "What?" (or whatever your
driver's default bad command message is) to Bozo.
</p>
<p>
Once the first command returns 1, the driver proceeds on to Bozo's second
command, following much the same structure. Note that with "tell descartes
when is the next reboot", the driver passes "descartes when is the next
reboot" to the function associated with tell. That function in turn has
to decide what to do with that argument. After that command returns either
1 or 0, the driver then proceeds on to the next living object with commands
pending, and so on until all living objects with pending commands have had
their commands performed.
</p>
<hr size="1">
<p><strong>2.4 The Efuns set_heart_beat() and call_out()</strong></p>
<p>
Once all commands are performed for objects with commands pending, the driver
then proceeds to call the heart_beat() function in all objects listed with the
driver as having heartbeats. Whenever an object calls the efun set_heart_beat()
with a non-zero argument (depending on your driver, what non-zero number may
be important, but in most cases you call it with the int 1). The efun
set_heart_beat() adds the object which calls set_heart_beat() to the list of
objects with heartbeats. If you call it with an argument of 0, then it removes
the object from the list of objects with heartbeats.
</p>
<p>
The most common use for heartbeats in the mudlib is to heal players and
monsters and perform combat. Once the driver has finished dealing with the
command list, it goes through the heartbeat list calling heart_beat() in
each object in the list. So for a player, for example, the driver will call
heart_beat() in the player which will:
</p>
<ul>
  <li>1) age the player
  <li>2) heal the player according to a heal rate
  <li>3) check to see if there are any hunted, hunting, or attacking objects around
  <li>4) perform an attack if step 3 returns true.
  <li>5) any other things which need to happen automatically roughly every second
</ul>
<p>
Note that the more objects which have heartbeats, the more processing
which has to happen every cycle the mud is up. Objects with heartbeats are
thus known as the major hog of CPU time on muds.
</p>
<p>
The call_out() efun is used to perform timed function calls which do not
need to happen as often as heartbeats, or which just happen once. Call outs
let you specify the function in an object you want called. The general formula
for call outs is: call_out(func, time, args); The third argument specifying
arguments is optional. The first argument is a string representing the name
of the function to be called. The second argument is how many seconds should
pass before the function gets called.
</p>
<p>
Practically speaking, when an object calls call_out(), it is added to
a list of objects with pending call outs with the amount of time of the
call out and the name of the function to be called. Each cycle of the driver,
the time is counted down until it becomes time for the function to be called.
When the time comes, the driver removes the object from the list of objects
with pending call outs and performs the call to the call out function, passing
any special args originally specified by the call out function.
</p>
<p>
If you want a to remove a pending call before it occurs, you need to use
the remove_call_out() efun, passing the name of the function being called
out. The driver will remove the next pending call out to that function.
This means you may have some ambiguity if more than one call out is pending
for the same function.
</p>
<p>
In order to make a call out cyclical, you must reissue the call_out()
efun in the function you called out, since the driver automatically removes
the function from the call out table when a call out is performed. Example:
<blockquote>
void foo() { call_out("hello", 10 ); }<br>
void hello() { call_out("hello", 10 ); }
</blockquote>
will set up hello() to be called every 10 seconds after foo() is first called.
</p>
<p>
There are several things to be careful about here. First, you must watch
to make sure you do not structure your call outs to be recursive in any
unintended fashion. Second, compare what a set_heart_beat() does when compared
directly to what call_out() does.
</p>
<p>set_heart_beat():</p>
<ul>
<li>a) Adds this_object() to a table listing objects with heartbeats.
<li>b) The function heart_beat() in this_object() gets called every single driver cycle.
</ul>

<p>call_out():</p>
<ul>
<li>a) Adds this_object(), the name of a function in this_object(), a time
delay, and a set of arguments to a table listing functions with pending
call outs.
<li>b) The function named is called only once, and that call comes after the
specified delay.
</ul>
<p>As you can see, there is a much greater memory overhead associated with
call outs for part (a), yet that there is a much greater CPU overhead associated
with heartbeats as shown in part (b), assuming that the delay for the call
out is greater than a single driver cycle.
</p>
<p>Clearly, you do not want to be issuing 1 second call outs, for then you
get the worst of both worlds. Similarly, you do not want to be having heart
beats in objects that can perform the same functions with call outs of a
greater duration than 1 second. I personally have heard much talk about
at what point you should use a call out over a heartbeat. What I have mostly
heard is that for single calls or for cycles of a duration greater than
10 seconds, it is best to use a call out. For repetitive calls of durations
less than 10 seconds, you are better off using heartbeats. I do not know
if this is true, but I do not think following this can do any harm.
</p>
<hr size="1">
<p><strong>2.5 Summary</strong></p>
<p>
Basic to a more in depth understanding of LPC is and understanding
of the way in which the driver interacts with the mudlib. You should now
understand the order in which the driver performs functions, as well as
a more detailed knowledge of the efuns this_player(), add_action(), and
move_object() and the lfun init(). In addition to this building upon knowledge
you got from the LPC Basics textbook, this chapter has introduced call outs
and heartbeats and the manner in which the driver handles them. You should
now have a basic understanding of call outs and heartbeats such that you
can experiment with them in your realm code.</p>
<p>

